博客
关于我
Netty堆外内存泄露排查与总结
阅读量:67 次
发布时间:2019-02-26

本文共 1170 字,大约阅读时间需要 3 分钟。

今天,我遇到了一个让人头疼的问题,Netty框架在运行过程中出现了堆外内存泄露,导致服务端 WebSocket 连接大量出现5xx错误。作为一个刚入行的开发人员,我对这种问题还不太熟悉,于是决定一步步深入排查,找到问题的根源并解决它。

首先,问题开始于Nginx出现大量5xx错误,这通常意味着服务端在处理请求时出现了问题。根据监控平台的日志,我注意到某台机器在同一时间点爆发了GC,并且 JVM 线程被阻塞。这让我怀疑是不是内存问题导致的。

接下来,我查看了日志,发现log4j2在大量打印日志,导致NIO线程被阻塞。这可能是因为log4j2的配置文件中没有正确注释掉控制台打印的日志模块,导致大量日志被同步打印,阻塞了线程。于是,我尝试注释掉这部分日志模块,但问题并没有解决,这让我开始怀疑还有其他隐藏的问题。

然后,我发现日志中出现了大量的failed to allocate 64(bytes) of direct memory错误,这表明堆外内存不足,Netty框架自己封装了一个OutOfDirectMemoryError。这让我意识到可能存在堆外内存泄露的问题,但之前的监控指标显示没有异常,我感到有些困惑。

进一步分析,我注意到Netty在分配堆外内存时使用了PlatformDependent.incrementMemory()方法,发现这是在堆外内存分配时触发的计数器检查。通过检查代码,我发现当堆外内存已使用超过用户指定的上限时,Netty会抛出自定义的OOM错误。这表明堆外内存确实没有被正确释放。

为了监控堆外内存的使用情况,我通过反射获取了DIRECT_MEMORY_COUNTER字段,并在控制台打印内存使用情况。初步发现内存在缓慢增长,随着时间的推移,内存使用量迅速升高,达到几百MB,最后导致OOM爆发。

为了找出内存泄露的具体原因,我在本地环境中模拟了客户端连接和关闭的过程,观察内存变化。发现每次客户端连接断开时,堆外内存会增加一段256B的内存,并且无法释放。这让我怀疑是在断开连接时发生了内存泄露。

通过使用Idea进行线上跟踪,我定位到在onDisconnect事件处理中,Netty框架在某个地方申请了堆外内存但没有正确释放。在进一步的调试中,我发现了一个潜在的NPE异常,发现了subType字段为null的情况,导致内存泄露。

最终,我修改了代码,确保在断开连接时subType字段被正确赋值,避免了NPE异常的发生。随后,我进行了本地和线上的验证,确认问题得到解决。

通过这次排查过程,我学会了如何一步步缩小问题范围,利用调试工具定位内存泄露的根源,并学会了如何通过反射监控堆外内存,确保框架的稳定性。这次经历让我对Netty框架的内存管理有了更深入的理解,也提升了我解决复杂问题的能力。

转载地址:http://aobz.baihongyu.com/

你可能感兴趣的文章
Netty源码—2.Reactor线程模型一
查看>>
Netty源码—3.Reactor线程模型三
查看>>
Netty源码—4.客户端接入流程一
查看>>
Netty源码—4.客户端接入流程二
查看>>
Netty源码—5.Pipeline和Handler一
查看>>
Netty源码—5.Pipeline和Handler二
查看>>
Netty源码—6.ByteBuf原理一
查看>>
Netty源码—6.ByteBuf原理二
查看>>
Netty源码—7.ByteBuf原理三
查看>>
Netty源码—7.ByteBuf原理四
查看>>
Netty源码—8.编解码原理一
查看>>
Netty源码—8.编解码原理二
查看>>
Netty源码解读
查看>>
Netty的Socket编程详解-搭建服务端与客户端并进行数据传输
查看>>
Netty相关
查看>>
Netty遇到TCP发送缓冲区满了 写半包操作该如何处理
查看>>
Netty:ChannelPipeline和ChannelHandler为什么会鬼混在一起?
查看>>
Netty:原理架构解析
查看>>
Network Dissection:Quantifying Interpretability of Deep Visual Representations(深层视觉表征的量化解释)
查看>>
Network Sniffer and Connection Analyzer
查看>>